package org.light.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.light.domain.ConfigFile;
import org.light.domain.Domain;
import org.light.domain.Statement;
import org.light.utils.WriteableUtil;


public class ApplicationContextXml extends ConfigFile{
	protected String packageToken;
	protected String domainSuffix = "domain";
	protected List<Statement> contents = new ArrayList<Statement>();
	protected List<Domain> domainList = new ArrayList<Domain>();
	protected Set<Action> actions = new TreeSet<Action>();
	protected Set<SpringMVCController>  controllers = new TreeSet<SpringMVCController>();
	protected List<String> packagesToScanList = new ArrayList<String>();
	protected String dbname = "database";
	protected String dbUsername = "root";	
	protected String dbPassword = "";
	protected String dbType = "mysql";
	protected boolean emptypassword = false;
	protected Domain templateDomain;
	
	protected String xmlDefinition = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	protected String beansDefinition = "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n" +
								  "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
								  "xmlns:aop=\"http://www.springframework.org/schema/aop\"\n" +
								  "xmlns:context=\"http://www.springframework.org/schema/context\"\n" +
								  "xmlns:tx=\"http://www.springframework.org/schema/tx\"\n" +
								  "xmlns:p=\"http://www.springframework.org/schema/p\"\n" +
								  "xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n" +
								  "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd\n" +
								  "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd\n" +
								  "http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd\">";
	
	protected String transactionManager = "\t<bean id=\"transactionManager\" class=\"org.springframework.orm.hibernate4.HibernateTransactionManager\">\n" +
										  "\t\t<property name=\"sessionFactory\" ref=\"sessionFactory\"></property>\n" +
										  "\t</bean>";
	
	protected String txAdvice = "\t<tx:advice id=\"txAdvice\" transaction-manager=\"transactionManager\">\n" +
								"\t<tx:attributes>\n" +
								"\t\t<tx:method name=\"list*\" read-only=\"true\"/>\n" +
								"\t\t<tx:method name=\"nameIsValid\" read-only=\"true\"/>\n" +
								"\t\t<tx:method name=\"*\"/>\n" +
								"\t</tx:attributes>\n" +
								"\t</tx:advice>";
	
	protected String aopConfig = "\t<aop:config>\n" +
								 "\t\t<aop:pointcut expression=\"execution(* org.asdmp.serviceimpl.*.*(..))\" id=\"txPointcut\"/>\n" +
								 "\t\t<aop:advisor advice-ref=\"txAdvice\" pointcut-ref=\"txPointcut\"/>\n" +
								 "\t</aop:config>\n";
	
	public String sqlDataSource(){ String result = "\t<bean class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\" id=\"dataSource\">\n"+
									 "\t\t<property value=\"com.mysql.jdbc.Driver\" name=\"driverClassName\"/>\n"+
									 "\t\t<property value=\"jdbc:mysql://localhost:3306/"+this.getDbname()+"?useUnicode=true&amp;characterEncoding=utf-8\" name=\"url\"/>\n"+								 
									 "\t\t<property value=\""+this.getDbUsername()+"\" name=\"username\"/>\n";
									 if (!this.isEmptypassword()) result += "\t\t<property value=\""+this.getDbPassword()+"\" name=\"password\"/>\n";
									 result += "\t</bean>\n";
								 String result2 = "\t<bean class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\" id=\"dataSource\">\n"+
										 "\t\t<property value=\"oracle.jdbc.driver.OracleDriver\" name=\"driverClassName\"/>\n"+
										 "\t\t<property value=\"jdbc:oracle:thin:@localhost:1521:"+this.getDbname() +"\" name=\"url\"/>\n"+								 
										 "\t\t<property value=\""+this.getDbUsername()+"\" name=\"username\"/>\n";
										 if (!this.isEmptypassword()) result2 += "\t\t<property value=\""+this.getDbPassword()+"\" name=\"password\"/>\n";
										 result2 += "\t</bean>\n";
								if (this.dbType!=null && ("mysql".equalsIgnoreCase(this.dbType)||"mariadb".equalsIgnoreCase(this.dbType))){
									 return result;
								}else if (this.dbType!=null && "oracle".equalsIgnoreCase(this.dbType)){
									return result2;
								}else if (this.dbType!=null && "postgresql".equalsIgnoreCase(this.dbType)||"pgsql".equalsIgnoreCase(this.dbType)){
									return getPostgreSQLDataSource();
								}else {
									return result;
								}
	}	

	private String getPostgreSQLDataSource() {
		List<Writeable> sList = new ArrayList<Writeable>();
		sList.add(new Statement(1000L,1,"<bean class=\"org.springframework.jdbc.datasource.DriverManagerDataSource\" id=\"dataSource\">"));
		sList.add(new Statement(2000L,2,"<property name=\"driverClassName\" value=\"org.postgresql.Driver\"/>"));
		sList.add(new Statement(3000L,2,"<property name=\"url\" value=\"jdbc:postgresql://localhost:5432/"+this.getDbname()+"?useUnicode=true&amp;characterEncoding=utf-8\"/>"));
		sList.add(new Statement(4000L,2,"<property name=\"username\" value=\""+this.getDbUsername()+"\"/>"));
		sList.add(new Statement(5000L,2,"<property name=\"password\" value=\""+this.getDbPassword()+"\"/>"));
		sList.add(new Statement(6000L,1,"</bean>"));
		return WriteableUtil.merge(sList).getContent();
	}

	protected String sqlSessionFactory() { return 	"\t<!-- define the SqlSessionFactory -->\n"+
											   "\t<bean id=\"sessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">\n"+
											   "\t\t<property name=\"dataSource\" ref=\"dataSource\" />\n" +
											   "\t\t<property name=\"typeAliasesPackage\" value=\""+this.packageToken+"."+this.domainSuffix+"\" />\n"+
											   "\t</bean>\n";
	}

	public String sqlTransactionManager() {return "\t<!-- transaction manager, use JtaTransactionManager for global tx -->\n" +
											 "\t<bean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n" +
										     "\t\t<property name=\"dataSource\" ref=\"dataSource\" />\n" +
										     "\t</bean>\n"; 
	}
	
	public String mybatisDaoScan(){ return "\t<!-- scan for mappers and let them be autowired -->\n"+
									  "\t<bean class=\"org.mybatis.spring.mapper.MapperScannerConfigurer\">\n"+
									  "\t\t<property name=\"basePackage\" value=\""+this.packageToken+"."+this.templateDomain.getDaoSuffix()+"\" />\n"+
									  "\t</bean>\n";
	}

	public String serviceImplScan() {return "\t<!-- enable component scanning (beware that this does not enable mapper scanning!) -->\n" +    
									   "\t<context:component-scan base-package=\""+this.packageToken+"."+this.templateDomain.getServiceimplSuffix()+"\" />\n";
	}
	
  	protected String autowire = "\t<!-- enable autowire -->\n"+
  								"\t<context:annotation-config />\n";
  	
    protected String annotationDriven = "\t<!-- enable transaction demarcation with annotations -->\n"+
    									"\t<tx:annotation-driven />";
    
    public String componentScan() { return "\t<context:component-scan base-package=\""+this.packageToken+"\" />";}

	public String getPackageToken() {
		return packageToken;
	}

	public void setPackageToken(String packageToken) {
		this.packageToken = packageToken;
	}

	public boolean isEmptypassword() {
		return emptypassword;
	}

	public void setEmptypassword(boolean emptypassword) {
		this.emptypassword = emptypassword;
	}

	public Set<Action> getActionList() {
		return actions;
	}

	public void setActionList(Set<Action> actionList) {
		this.actions = actionList;
	}
	
	public void addAction(Action action){
		this.actions.add(action);
	}

	public List<String> getPackagesToScanList() {
		return packagesToScanList;
	}

	public void setPackagesToScanList(List<String> packagesToScanList) {
		this.packagesToScanList = packagesToScanList;
	}

	public List<Domain> getDomainList() {
		return domainList;
	}
	
	public void addDomain(Domain domain){
		this.domainList.add(domain);
		if (domain!= null && this.packageToken == null){
			this.packageToken = domain.getPackageToken();
		}
		if (domain!= null && this.domainSuffix == null){
			this.domainSuffix = domain.getDomainSuffix();
		}
	}

	public void setDomainList(List<Domain> domainList) {
		this.domainList = domainList;
		if (domainList!= null && domainList.size() >0 ){
			this.packageToken = domainList.get(0).getPackageToken();
			this.domainSuffix = domainList.get(0).getDomainSuffix();
		}
	}

	public String getDbname() {
		return this.dbname;
	}

	public void setDbname(String dbname) {
		this.dbname = dbname;
	}

	public String getDbUsername() {
		return dbUsername;
	}

	public void setDbUsername(String dbUsername) {
		this.dbUsername = dbUsername;
	}
	
	public void addFacade(SpringMVCController controller){
		this.controllers.add(controller);
	}

	public String getDbPassword() {
		return dbPassword;
	}

	public void setDbPassword(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public String getXmlDefinition() {
		return xmlDefinition;
	}

	public void setXmlDefinition(String xmlDefinition) {
		this.xmlDefinition = xmlDefinition;
	}

	public ApplicationContextXml(){
		super();
		this.setStandardName("applicationContext.xml");
		this.setPutInsideSrcAndClasses(true);
	}
	
	public ApplicationContextXml(String packageToken,String dbname,String dbUsername,String dbPassword){
		this(packageToken,dbname,dbUsername,dbPassword,"mysql");

	}

	public ApplicationContextXml(String packageToken,String dbname,String dbUsername,String dbPassword,String dbType){
		super();
		this.setPackageToken(packageToken);
		this.setDbname(dbname);
		this.setDbUsername(dbUsername);
		this.setDbPassword(dbPassword);
		this.setDbType(dbType);
		this.setStandardName("applicationContext.xml");
		this.setPutInsideSrcAndClasses(true);
	}
	
	@Override
	public String generateConfigFileString() {
		StringBuilder sb = new StringBuilder();
		sb.append(this.xmlDefinition).append("\n").append(this.beansDefinition).append("\n");
		sb.append(this.sqlDataSource()).append("\n");
		sb.append(this.sqlSessionFactory()).append("\n");
		sb.append(this.sqlTransactionManager()).append("\n");
		sb.append(this.mybatisDaoScan()).append("\n");
//		sb.append(this.serviceImplScan()).append("\n");
		sb.append(this.autowire).append("\n");
		sb.append(this.annotationDriven).append("\n");
//		sb.append(txAdvice).append("\n");
//		sb.append(aopConfig).append("\n");
//		for (Domain d:this.domainList){
//			sb.append(S2SMXmlGenerator.generateApplicationContentXmlServiceBeansStatementList(1000L, 1, d).getContent());
//		}
//		for (Action ac:this.actions){
//			sb.append(S2SMXmlGenerator.generateApplicationContentXmlActionBeansStatementList(1000L, 1, ac).getContent());
//		}
//		for (SpringMVCFacade sf:this.facades){
//			sb.append(S2SMXmlGenerator.generateApplicationContentXmlFacadeBeansStatementList(1000L, 1, sf).getContent());
//		}
		sb.append(this.componentScan()).append("\n");
		sb.append("</beans>").append("\n");
		return sb.toString();
	}

	public String getDbType() {
		return dbType;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}

	public Set<SpringMVCController> getControllers() {
		return controllers;
	}

	public void setControllers(Set<SpringMVCController> controllers) {
		this.controllers = controllers;
	}

	public String getDomainSuffix() {
		return domainSuffix;
	}

	public void setDomainSuffix(String domainSuffix) {
		this.domainSuffix = domainSuffix;
	}

	public Domain getTemplateDomain() {
		return templateDomain;
	}

	public void setTemplateDomain(Domain templateDomain) {
		this.templateDomain = templateDomain;
	}
}
